// node iterator_demo.js
// npx babel-node iterator_demo.js

let _ = "";
const {log} = console;

let test1 = function(){
    let makeIterator = function(array){
        let nextIndex = 0;
        return {
            next(){
                return nextIndex < array.length ? 
                    {value: array[nextIndex++], done: false} :
                    {value: undefined, done: true};
            }
        };
    };

    let it = makeIterator(['a', 'b']);

    _ = it.next();
    log(_)
    _ = it.next();
    log(_)
    _ = it.next();
    log(_)
};
// test1();

let test2 = function(){
    const obj = {
        [Symbol.iterator]: function(){
            return {
                next: function(){
                    return {
                        value: 1,
                        done: true
                    };
                }
            };
        }
    };
    log(obj);
    for (let x of obj) {
        log(x)
    }
};
// test2();

let test02 = function(){
    const obj = {
        [Symbol.iterator]() {return this;},
        next(){
            return {
                value: 1,
                done: true
            };
        }
    };
    log(obj);
    for (let x of obj) {
        log(x)
    }
};
// test02();

// 必须要有 if else 两种情形才能成功吗?
let test2_02 = function(){
    const obj = {
        [Symbol.iterator](){
            let index = 0;
            return {
                next(){
                    if (index == 0) {
                        index++;
                        return {
                            value: 1,
                            done: false
                        };
                    } else {
                        return {value: undefined, done: true};
                    }
                }
            };
        }
    };
    for (let x of obj) {
        log(x)
    }
};
// test2_02();


let test3 = function(){
    let arr = ['a', 'b', 'c'];
    let iter = arr[Symbol.iterator]();

    _ = iter.next();
    log(_);
    _ = iter.next();
    log(_);
    _ = iter.next();
    log(_);
    _ = iter.next();
    log(_);
};
// test3();

// 一个对象如果要具备可被for...of循环调用的 Iterator 接口，就必须在Symbol.iterator的属性上部署遍历器生成方法（原型链上的对象具有该方法也可）。

let test4 = function(){
    class RangeIterator {
        constructor(start, stop) {
            this.value = start;
            this.stop = stop;
        }
        [Symbol.iterator](){ return this; }
        next(){
            let value = this.value;
            if (value < this.stop) {
                this.value++;
                return {done: false, value: value};
            }
            return {done: true, value: undefined};
        }
    }

    let range = function(start, stop){
        return new RangeIterator(start, stop);
    };

    for (let val of range(0, 3)) {
        log(val)
    }
};
// test4();

let test5 = function(){
    let Obj = function(value){
        this.value = value;
        this.next = null;
    };
    Obj.prototype[Symbol.iterator] = function(){
        let current = this;
        let fn_next = function(){
            if (current) {
                let value = current.value;
                current = current.next;
                return { done: false, value: value };
            } else {
                return { done: true };
            }
        };
        let iterator = {next: fn_next};
        return iterator;
    };

    let one = new Obj(1);
    let two = new Obj(2);
    let three = new Obj(3);

    one.next = two;
    two.next = three;

    for(let i of one) {
        log(i)
    }
};
// test5();

let test6 = function(){
    let obj = {
        data: ['hello', 'world'],
        [Symbol.iterator](){
            const self = this;
            let index = 0;
            return {
                next() {
                    if (index < self.data.length) {
                        return {
                            value: self.data[index++],
                            data: false
                        }
                    } else {
                        return { value: undefined, done: true };
                    }
                }
            };
        }
    };
    for (let x of obj) {
        log(x)
    }
};
// test6();

// 类似数组的对象调用数组的Symbol.iterator方法的例子
let test7 = function(){
    let iterable = {
        0: 'a',
        1: 'b',
        2: 'c',
        length: 3,
        [Symbol.iterator]: Array.prototype[Symbol.iterator]
    };
    for (let item of iterable) {
        log(item)
    }
};
// test7();

// 注意，普通对象部署数组的Symbol.iterator方法，并无效果。
let test8 = function(){
    let iterable = {
        a: 'a',
        b: 'b',
        c: 'c',
        length: 3,
        [Symbol.iterator]: Array.prototype[Symbol.iterator]
    };
    for (let item of iterable) {
        log(item)
    }
};
// test8();

// 对数组和 Set 结构进行解构赋值时，会默认调用Symbol.iterator方法。
let test9 = function(){
    let set = new Set().add('a').add('b').add('c');

    let [x, y] = set;
    log(x, y);

    let [first, ...rest] = set;
    log(first, rest);
};
// test9();

// 扩展运算符（...）也会调用默认的 Iterator 接口。
let test10 = function(){
    let str = 'hello';
    _ = [...str];
    log(_)

    let arr = ['b', 'c'];
    _ = ['a', ...arr, 'd']
    log(_)
};
// test10();

// yield*后面跟的是一个可遍历的结构，它会调用该结构的遍历器接口。
let test11 = function(){
    let generator = function*(){
        yield 1;
        yield* [2, 3, 4];
        yield 5;
    };

    let iterator = generator();

    _ = iterator.next();
    while(!_.done) {
        log(_);
        _ = iterator.next();
    }
    log(_);
};
// test11();

// 字符串是一个类似数组的对象，也原生具有 Iterator 接口。
let test12 = function(){
    let someString = "hi";
    _ = typeof someString[Symbol.iterator];
    log(_)

    let iterator = someString[Symbol.iterator]();
    _ = iterator.next();
    while(!_.done){
        log(_);
        _ = iterator.next();
    }
    log(_);
};
// test12();

// 可以覆盖原生的Symbol.iterator方法，达到修改遍历器行为的目的。
let test13 = function(){
    let str = new String("hi");
    log(str.toString());

    _ = [...str];
    log(_);

    str[Symbol.iterator] = function(){
        return {
            next(){
                if (this._first) {
                    this._first = false;
                    return { value: "bye", done: false };
                } else {
                    return { done: true };
                }
            },
            _first: true
        };
    };

    _ = [...str];
    log(_);

    log(str);
    log(str.toString());
};
// test13();

// Symbol.iterator方法的最简单实现，还是使用下一章要介绍的 Generator 函数。
let test14 = function(){
    let myIterable = {
        [Symbol.iterator]: function* () {
            yield 1;
            yield 2;
            yield 3;
        }
    };

    _ = [...myIterable];
    log(_);

    let obj = {
        * [Symbol.iterator]() {
            yield 'hello';
            yield 'world';
        }
    };

    for (let x of obj) {
        log(x);
    }
};
// test14();

// 数组原生具备iterator接口（即默认部署了Symbol.iterator属性），for...of循环本质上就是调用这个接口产生的遍历器，可以用下面的代码证明。
let test15 = function(){
    const arr = ['red', 'green', 'blue'];

    for(let v of arr) {
        log(v);
    }

    const obj = {};
    obj[Symbol.iterator] = arr[Symbol.iterator].bind(arr);

    for(let v of obj) {
        log(v);
    }
};
// test15();

// 并不是所有类似数组的对象都具有 Iterator 接口，一个简便的解决方法，就是使用Array.from方法将其转为数组。

let test16 = function(){
    let arrayLike = { length: 2, 0: 'a', 1: 'b' };

    // 报错
    // for (let x of arrayLike) {
    //     log(x);
    // }

    for (let x of Array.from(arrayLike)) {
        log(x);
    }
};
// test16();

// 对于普通的对象，for...in循环可以遍历键名，for...of循环会报错。
// 使用 Generator 函数将对象重新包装一下。
let test17 = function(){
    let es6 = {
        edition: 6,
        committee: 'TC39',
        standard: "ECMA-262"
    };
    let entries = function*(obj){
        for (let key of Object.keys(obj)) {
            yield [key, obj[key]];
        }
    };
    for(let [key, val] of entries(es6)) {
        log(key, '->', val);
    }
};
test17();
















